﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;

namespace pmf
{
    public class LogText
    {
        object      _lock           = new object();     //  bloqueo
        Encoding    _codificacion   = Encoding.UTF8;    //  codificación en que se guardan las cadenas
        uint        _maxLineas      = 100;              //  máxima cantidad de líneas que se dejan despues de llamar a "mantener()"
        uint        _maxBackupFiles = 40;               //  archivos de respaldo..
        uint        _mantenerCada   = 1;                //  cantidad de inserciones que desencadenan un mantenimiento automático
        string      _nombreArchivo  = string.Empty;     //  nombre del archivo de log
        uint        _contInserts    = 0;                //  cuenta las inserciones para saber cuando mantener automaticamente
        FileStream  _fileStream     = null;             //  stream del archivo de log

        public LogText(
            string      nombreArchivo,
            uint        maxLineas,
            uint        mantenerCada,
            Encoding    codificacion
        )
        {
            _maxLineas      = Math.Max(maxLineas, 1);
            _mantenerCada   = Math.Max(mantenerCada, 1);
            _nombreArchivo  = nombreArchivo;
            _codificacion   = codificacion;
            abrirArchivo();
        }

        public bool loguear(Exception e)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(e.ToString());
            sb.Append("\r\n");
            sb.Append(e.StackTrace);
            return loguear(sb.ToString(), true);
        }

        public bool loguear(string s)
        {
            return loguear(s, false);
        }

        public bool loguear(string s, bool esExcepcion)
        {
            try
            {
                lock (_lock)
                {
                    if (_contInserts > 0)
                        if ((_contInserts % _mantenerCada) == 0)
                            mantener();
                    escribirArchivo(armarCadena(s, esExcepcion));
                    _contInserts++;
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }

        public void mantener()
        {
            lock (_lock)
            {
                cerrarArchivo();
                if (esArchivoGrande())
                    File.Move(
                        _nombreArchivo,
                        _nombreArchivo + string.Format(
                            "_BACKUP_{0:yyyyMMddHHmmss}",
                            DateTime.Now
                        )
                    );
                abrirArchivo();
                eliminarViejos();
            }
        }

        public object dameLock()
        {
            return _lock;
        }

        public string dameContenido()
        {
            string contenido = string.Empty;

            lock (_lock)
            {
                cerrarArchivo();
                contenido = File.ReadAllText(
                    _nombreArchivo
                );
                abrirArchivo();
            }

            return contenido;
        }

        public List<string> dameRenglones()
        {
            List<string> ret = new List<string>();

            lock (_lock)
            {
                cerrarArchivo();
                ret.AddRange(File.ReadAllLines(
                    _nombreArchivo
                ));
                abrirArchivo();
            }

            return ret;
        }

        public string path
        {
            get { return _nombreArchivo; }
        }

        string armarCadena(string linea, bool esExcepcion)
        {
            return string.Format(
                "{0:yyyy/MM/dd HH:mm:ss} {1}{2}",
                DateTime.Now,
                (esExcepcion ? "EXCEPT!!! " : "") + linea,
                "\r\n"
            );
        }

        bool esArchivoGrande()
        {
            int count = 0;

            using (StreamReader sr = new StreamReader(_nombreArchivo))
            {
                while (null != sr.ReadLine())
                    count++;
            }

            return count >= _maxLineas;
        }

        void eliminarViejos()
        {
            List<string> archivos = new List<string>(Directory.GetFiles(
                Path.GetDirectoryName(_nombreArchivo),
                Path.GetFileName(_nombreArchivo) + "_BACKUP*"
            ));

            if (archivos.Count > _maxBackupFiles)
            {
                archivos.Sort();

                while (archivos.Count > _maxBackupFiles)
                {
                    File.Delete(archivos[0]);
                    archivos.Remove(archivos[0]);
                }
            }
        }

        bool abrirArchivo()
        {
            try
            {
                // abro el archivo para agregar datos...
                _fileStream = File.Open(
                    _nombreArchivo,
                    FileMode.Append,
                    FileAccess.Write,
                    FileShare.ReadWrite
                );
                return true;
            }
            catch
            {
                return false;
            }
        }

        void cerrarArchivo()
        {
            if (null != _fileStream)
            {
                _fileStream.Flush();
                _fileStream.Close();
            }
        }

        void escribirArchivo(string cadena)
        {
            byte[] buffer = _codificacion.GetBytes(cadena);
            _fileStream.Write(buffer, 0, buffer.Length);
            _fileStream.Flush();
        }
    }
}
